/**
 * 
 */
package com.google.code.jkippt;

import static com.google.code.jkippt.http.ParametersBuilder.values;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;

import com.google.code.jkippt.annotation.Experimental;
import com.google.code.jkippt.http.APIClient;
import com.google.code.jkippt.http.ParametersBuilder;
import com.google.code.jkippt.parser.Parser;
import com.google.code.jkippt.resource.Clip;
import com.google.code.jkippt.resource.ClipList;
import com.google.code.jkippt.resource.User;

/**
 * @author "Federico De Faveri defaveri@gmail.com"
 *
 */
public class Users extends KipptEndpoint {
	
	public static final String USERS_ENDPOINT_NAME = "users";	

	protected static final String FOLLOWERS = "followers";
	protected static final String FOLLOWING = "following";
	protected static final String RELATIONSHIP = "relationship";
	protected static final String CLIPS = "clips";
	protected static final String FAVORITES_CLIPS = "clips/favorites";
	protected static final String LIKES_CLIPS = "clips/likes";
	protected static final String LISTS = "lists";
	protected static final String SEARCH = "search";
	protected static final String FOLLOW_ACTION = "{\"action\": \"follow\"}";
	protected static final String UNFOLLOW_ACTION = "{\"action\": \"unfollow\"}";
	protected static final String INCLUDE_DATA = "include_data";
	protected static final String[] INCLUDE_LABELS = new String[]{"list","via"};

	protected ResourceEndpoint<User> usersEndpoint;
	protected ResourceEndpoint<Clip> clipEndpoint;
	protected ResourceEndpoint<ClipList> listEndpoint;

	/**
	 * @param client
	 * @param parser
	 */
	protected Users(APIClient client, Parser parser) {
		super(client, parser, USERS_ENDPOINT_NAME);
		usersEndpoint = new ResourceEndpoint<User>(client, parser, endpointUrlFactory, User.class);
		clipEndpoint = new ResourceEndpoint<Clip>(client, parser, endpointUrlFactory, Clip.class);
		listEndpoint = new ResourceEndpoint<ClipList>(client, parser, endpointUrlFactory, ClipList.class);
	}

	/**
	 * Gets the specified {@link User}. The {@link User} can be retrieved both by id or by username.
	 * @param id the {@link User} id or username.
	 * @return the retrieved {@link User}.
	 * @throws JKipptException if an error occurs.
	 */
	public User get(String id) throws JKipptException
	{
		return usersEndpoint.get(id);
	}

	/**
	 * Returns all the specified {@link User} followers.
	 * @param id the {@link User} id.
	 * @return the {@link User} followers.
	 * @throws JKipptException if an error occurs retrieving {@link User} followers.
	 */
	public Iterator<User> getFollowers(String id) throws JKipptException
	{
		String firstPageEndpoint = endpointUrlFactory.buildEndpointUrl(id, FOLLOWERS);
		return usersEndpoint.getPaginated(firstPageEndpoint);
	}

	/**
	 * Returns all the {@link User} followed by the specified {@link User}.
	 * @param id the {@link User} id.
	 * @return the {@link User}s followed users.
	 * @throws JKipptException if an error occurs retrieving {@link User} followed.
	 */
	public Iterator<User> getFollowing(String id) throws JKipptException
	{
		String firstPageEndpoint = endpointUrlFactory.buildEndpointUrl(id, FOLLOWING);
		return usersEndpoint.getPaginated(firstPageEndpoint);
	}

	/**
	 * Gets information about a relationship to another user.
	 * @param id the other {@link User} id.
	 * @return <code>true</code> if there is a relationship, <code>false</code> otherwise.
	 * @throws JKipptException if an error occurs retrieving {@link User} relationship.
	 */
	public boolean getRelationship(String id) throws JKipptException
	{
		String endpoint = endpointUrlFactory.buildEndpointUrl(id, RELATIONSHIP);
		String json = client.get(endpoint);
		return parser.parseRelationship(json);
	}

	/**
	 * Gets user's public clips. 
	 * @param id the user id.
	 * @return the public clips.
	 * @throws JKipptException
	 */
	public Iterator<Clip> getClips(String id) throws JKipptException
	{
		return getClips(id, false, false);
	}

	/**
	 * Gets user's public clips. 
	 * @param id the user id.
	 * @param includeVia <code>true</code> to include Via data.
	 * @param includeList <code>true</code> to include List data.
	 * @return the public clips.
	 * @throws JKipptException
	 */
	public Iterator<Clip> getClips(String id, boolean includeVia, boolean includeList) throws JKipptException
	{
		ParametersBuilder builder = new ParametersBuilder();
		builder.add(INCLUDE_DATA, values(includeList, includeVia), INCLUDE_LABELS);
		String endpoint = endpointUrlFactory.buildEndpointUrl(Arrays.asList(id, CLIPS), builder.build());
		return clipEndpoint.getPaginated(endpoint);
	}

	/**
	 * Gets user's public favorites clips. 
	 * @param id the user id.
	 * @return the public favorites clips.
	 * @throws JKipptException
	 */
	public Iterator<Clip> getFavoritesClips(String id) throws JKipptException
	{
		return getFavoritesClips(id, false, false);
	}

	/**
	 * Gets user's public favorites clips. 
	 * @param id the user id.
	 * @param includeVia <code>true</code> to include Via data.
	 * @param includeList <code>true</code> to include List data.
	 * @return the public favorites clips.
	 * @throws JKipptException
	 */
	public Iterator<Clip> getFavoritesClips(String id, boolean includeVia, boolean includeList) throws JKipptException
	{
		ParametersBuilder builder = new ParametersBuilder();
		builder.add(INCLUDE_DATA, values(includeList, includeVia), INCLUDE_LABELS);
		String endpoint = endpointUrlFactory.buildEndpointUrl(Arrays.asList(id, FAVORITES_CLIPS), builder.build());
		return clipEndpoint.getPaginated(endpoint);
	}

	/**
	 * Gets user's public likes.. 
	 * @param id the user id.
	 * @return the public likes clips.
	 * @throws JKipptException
	 */
	public Iterator<Clip> getLikesClips(String id) throws JKipptException
	{
		return getLikesClips(id, false, false);
	}

	/**
	 * Gets user's public likes.. 
	 * @param id the user id.
	 * @param includeVia <code>true</code> to include Via data.
	 * @param includeList <code>true</code> to include List data.
	 * @return the public likes clips.
	 * @throws JKipptException
	 */
	public Iterator<Clip> getLikesClips(String id, boolean includeVia, boolean includeList) throws JKipptException
	{
		ParametersBuilder builder = new ParametersBuilder();
		builder.add(INCLUDE_DATA, values(includeList, includeVia), INCLUDE_LABELS);
		String endpoint = endpointUrlFactory.buildEndpointUrl(Arrays.asList(id, LIKES_CLIPS), builder.build());
		return clipEndpoint.getPaginated(endpoint);
	}

	/**
	 * Gets user's public lists 
	 * @param id the user id.
	 * @return the public clip lists.
	 * @throws JKipptException
	 */
	public Iterator<ClipList> getLists(String id) throws JKipptException
	{
		String endpoint = endpointUrlFactory.buildEndpointUrl(id, LISTS);
		return listEndpoint.getPaginated(endpoint);
	}

	/**
	 * Return the user's specified list. 
	 * @param id the user id.
	 * @param listId the list id.
	 * @return the list.
	 * @throws JKipptException
	 */
	public ClipList getList(String id, String listId) throws JKipptException
	{
		String endpoint = endpointUrlFactory.buildEndpointUrl(id, LISTS, listId);
		return listEndpoint.getFromEndpoint(endpoint);
	}

	/**
	 * Search all the {@link User}s with the specified query term.
	 * Index is build out of username, description and social profiles.
	 * @param query the query.
	 * @return the found {@link User}s.
	 * @throws JKipptException if an error occurs retrieving the {@link User}s.
	 * @Experimental This is currently being tested and should be considered as unsupported endpoint.
	 */
	@Experimental
	public Iterator<User> search(String query) throws JKipptException
	{
		String firstPageEndpoint = endpointUrlFactory.buildEndpointUrl(SEARCH, Collections.singletonMap("q", query));
		return usersEndpoint.getPaginated(firstPageEndpoint);
	}

	/**
	 * Make the user following the specified user.
	 * @param id the id of the user to follow.
	 * @return the updated status.
	 * @throws JKipptException if an error occurs during the relationship update.
	 */
	public boolean follow(String id) throws JKipptException
	{
		String endpoint = endpointUrlFactory.buildEndpointUrl(id, RELATIONSHIP);

		String json = client.post(endpoint, FOLLOW_ACTION);
		return parser.parseRelationship(json);
	}

	/**
	 * Make the user unfollowing the specified user.
	 * @param id the id of the user to unfollow.
	 * @return the updated status.
	 * @throws JKipptException if an error occurs during the relationship update.
	 */
	public boolean unfollow(String id) throws JKipptException
	{
		String endpoint = endpointUrlFactory.buildEndpointUrl(id, RELATIONSHIP);
		String json = client.post(endpoint, UNFOLLOW_ACTION);
		return parser.parseRelationship(json);
	}

}
